
Reflect.defineProperty(String.prototype, 'decodeAtoB', {
	get(this: String){
		return decodeURIComponent(atob(this.valueOf()));
	},
	enumerable: true,
	configurable: false,
});
Reflect.defineProperty(String.prototype, 'encodeBtoA', {
	get(this: String){
		return btoa(encodeURIComponent(this.valueOf()));
	},
	enumerable: true,
	configurable: false,
});

((_global_)=>{
	if(!Reflect.has(_global_, 'mtec')){
		Reflect.set(_global_, 'mtec', {});
		Reflect.set(mtec, '__mtec__', 'Many technologies');
	}

	if(!mtec.string){
		Reflect.set(mtec, 'string', {});
	}
	Reflect.set(mtec.string, '__space_description__', 'This is a mtec subnamespace for string');
})(window ?? globalThis);

mtec.string.random = function(len, radix){
	[len, radix] = [[Number(len), 6], [Number(radix), 10]].map(_=>isNaN(_[0]) ? _[1] : _[0]);
	radix = Math.max(2, Math.min(36, radix));

	let str = Math.random().toString(radix).slice(2, 2+len);
	while(str.length < len){
		str += Math.random().toString(radix).slice(2, 2+len-str.length);
	}
	return str;
}

mtec.string.randomToken = function(len, radix, verify){
	[radix, verify] = mtec.pickValueByType([radix, verify], [['number', 10], ['function', undefined]]) as [number, (token: string)=>boolean];
	let token = mtec.string.random(len, radix);

	if(typeof verify == 'function'){
		while(!verify(token)){
			token = mtec.string.random(len, radix);
		}
	}
	return token;
}

mtec.string.getAffix = function(list){
	if(!list || list.length==0) return {prefix: '', suffix: '', max_length: 0};

	let prefix = list[0];
	let suffix = list[0];
	let pre_end = prefix.length;
	let suf_start = 0;
	let max_length = 0;

	list.forEach(el=>{
		if(el.length>max_length) max_length = el.length;

		if(!el.includes(prefix)){
			while(pre_end>0 && !el.includes(prefix.slice(0, pre_end))) pre_end--;
			prefix = prefix.slice(0, pre_end);
		}

		if(!el.includes(suffix)){
			while(suf_start<suffix.length-1 && !el.includes(suffix.slice(suf_start, suffix.length))) suf_start++;
			suffix = suffix.slice(suf_start, suffix.length);
		}
	});

	return {prefix, suffix, max_length};
}

mtec.string.levenshtein = function(a, b){
	const matrix: number[][] = [];
	let i: number;
	let j: number;
	for(i = 0; i <= a.length; i++) matrix[i] = [i];
	for(j = 0; j <= b.length; j++) matrix[0][j] = j;

	for(i = 1; i <= a.length; i++){
		for(j = 1; j <= b.length; j++){
			if(b.charAt(j-1) == a.charAt(i-1)) matrix[i][j] = matrix[i-1][j-1];
			else{
				matrix[i][j] = Math.min(
					matrix[i-1][j],
					matrix[i][j-1],
					matrix[i-1][j-1]
				) + 1;
			}
		}
	}

	return matrix[a.length][b.length];
}

mtec.string.findLikeStr = function(target, list){
	let mostSimilar = list[0];
	let minDistance = mtec.string.levenshtein(mostSimilar, target);

	for (const str of list) {
	  if (str === target) {
		return str;
	  }

	  const distance = mtec.string.levenshtein(str, target);
	  if (distance < minDistance) {
		minDistance = distance;
		mostSimilar = str;
	  }
	}

	return mostSimilar;
}